package com.nuoniu.sibanyun.service.finance.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nuoniu.sibanyun.common.exception.NuoNiuException;
import com.nuoniu.sibanyun.entity.dto.ReceivableVerification;
import com.nuoniu.sibanyun.entity.erp.ErpNumberSet;
import com.nuoniu.sibanyun.entity.finance.VReceivePaymentMain;
import com.nuoniu.sibanyun.entity.finance.VReceivablePayableMain;
import com.nuoniu.sibanyun.entity.finance.VReceivePaymentPlan;
import com.nuoniu.sibanyun.mapper.erp.ErpNumberSetMapper;
import com.nuoniu.sibanyun.mapper.finance.VReceivablePayableMainMapper;
import com.nuoniu.sibanyun.service.erp.IErpAccountService;
import com.nuoniu.sibanyun.service.erp.IErpNumberSetService;
import com.nuoniu.sibanyun.service.finance.*;
import com.nuoniu.sibanyun.userThread.UserInfo;
import com.nuoniu.sibanyun.userThread.UserThreadLocal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 应收单主表 服务实现类
 * </p>
 *
 * @author 小啊刚
 * @since 2021-04-19
 */
@Service
public class VReceivablePayableMainServiceImpl extends ServiceImpl<VReceivablePayableMainMapper, VReceivablePayableMain> implements IVReceivablePayableMainService {

    @Autowired
    private IErpNumberSetService numberSetService;
    @Autowired
    private ErpNumberSetMapper numberSetMapper;
    @Autowired
    private IErpAccountService erpAccountService;
    @Autowired
    private IVReceivablePayableDetailService receivableDetailService;
    @Autowired
    private IVReceivePaymentPlanService planService;
    @Autowired
    private IVReceivePaymentMainService paymentMainService;

    @Override
    public String getCode(String sourceCode) {
        Integer currentAccountId = erpAccountService.getCurrentAccountId();
        Integer companyId = UserThreadLocal.get().getCompanyId();
        ErpNumberSet numberSet = numberSetMapper.getByCode(sourceCode, companyId, currentAccountId);
        String dateStr = numberSet.getMedian() == 1 ? "%Y-%m-%d" : "%Y-%m";
        String code = baseMapper.getNewestCode(companyId, dateStr, sourceCode);
        return numberSetService.getCode(numberSet, code);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveReceivable(VReceivablePayableMain receivableMain) {
        UserInfo userInfo = UserThreadLocal.get();
        if(receivableMain.getId() == null) {
            receivableMain.setVoucherCode(getCode(receivableMain.getDataSource()));
            receivableMain.setAccountId(erpAccountService.getCurrentAccountId());
            receivableMain.setCompanyId(userInfo.getCompanyId());
            receivableMain.setCreateUserId(userInfo.getUserId());
            receivableMain.setCreateUserName(userInfo.getRealName());
            receivableMain.setVoucherState(-1);
            receivableMain.setVerificationAmount(new BigDecimal(0.0));
        }else {
            VReceivablePayableMain receivable = this.getById(receivableMain.getId());
            if(!userInfo.getCompanyId().equals(receivable.getCompanyId())) {
                throw new NuoNiuException("你无此权限，请联系管理员");
            }

            receivableMain.setUpdateUserId(userInfo.getUserId());
            receivableMain.setUpdateUserName(userInfo.getRealName());
            receivableMain.setUpdateDate(LocalDateTime.now());
        }

        Boolean result = this.saveOrUpdate(receivableMain);
        if(result) {
            List<Long> detailIds = new ArrayList<>();
            receivableMain.getOrderDetailList().forEach(r -> {
                r.setOrderId(receivableMain.getId().intValue());
                r.setCompanyId(userInfo.getCompanyId());
                r.setAccountId(receivableMain.getAccountId());
                if(r.getId() != null) {
                    detailIds.add(r.getId());
                }
            });

            //删除明细
            QueryWrapper wrapper = new QueryWrapper();
            wrapper.eq("company_id", userInfo.getCompanyId());
            wrapper.eq("order_id", receivableMain.getId());
            if(detailIds.size() > 0) {
                wrapper.notIn("id", detailIds);
            }

            //删除多余明细
            receivableDetailService.remove(wrapper);
            //保存单据明细表
            receivableDetailService.saveOrUpdateBatch(receivableMain.getOrderDetailList());

            //保存收款计划
            if(receivableMain.getDataSource().equals("ReceivableVoucher") || receivableMain.getDataSource().equals("PayableVoucher")
                    && receivableMain.getPlanList().size() == 0) {
                List<Long> planIds = new ArrayList<>();
                BigDecimal amount = new BigDecimal(0.0);
                receivableMain.getPlanList().forEach(p -> {
                    p.setOrderId(receivableMain.getId().intValue());
                    if(p.getId() != null) {
                        planIds.add(p.getId());
                        amount.add(p.getAmount());
                    }
                });

                QueryWrapper planWrapper = new QueryWrapper();
                planWrapper.eq("order_id", receivableMain.getId());
                if(planIds.size() > 0) {
                    planWrapper.notIn("id", planIds);
                }

                //删除多余计划
                planService.remove(planWrapper);
                //保存计划
                planService.saveOrUpdateBatch(receivableMain.getPlanList());
            }
        }
    }

    @Override
    public void examine(Long id, Integer status) {
        if(null == id || null == status ||  (status > 1 || status < 0) )throw new NuoNiuException("参数错误");
        UserInfo userInfo = UserThreadLocal.get();
        VReceivablePayableMain receivableMain = this.getById(id);
        if(receivableMain == null) {
            throw new NuoNiuException("单据不存在");
        }

        if(!userInfo.getCompanyId().equals(receivableMain.getCompanyId())) {
            throw new NuoNiuException("你无此权限，请联系管理员");
        }

        if(status == 1){
            if(receivableMain.getVoucherState() > 0) throw new NuoNiuException("该单据已经审核");
            if(receivableMain.getVoucherState() < 0) throw new NuoNiuException("该单据还未提交");
        }else if(status == 0){
            if (receivableMain.getVoucherState() > 1)throw new NuoNiuException("该单据不是审核状态，不能弃审");
        }

        receivableMain.setVoucherState(status);
        receivableMain.setReviewerDate(LocalDateTime.now());
        receivableMain.setReviewerUserId(userInfo.getUserId());
        receivableMain.setReviewerUserName(userInfo.getRealName());
        this.updateById(receivableMain);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void deleteById(Long id) {
        UserInfo userInfo = UserThreadLocal.get();
        VReceivablePayableMain receivableMain = this.getById(id);
        if(receivableMain == null) {
            throw new NuoNiuException("单据不存在");
        }

        if(!userInfo.getCompanyId().equals(receivableMain.getCompanyId())) {
            throw new NuoNiuException("你无此权限，请联系管理员");
        }

        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("order_id", id);
        receivableDetailService.remove(wrapper);
        this.removeById(id);
    }

    @Override
    public void book(Long id) {
        UserInfo userInfo = UserThreadLocal.get();
        VReceivablePayableMain receivableMain = this.getById(id);
        if(receivableMain.getVoucherState().equals(0)) {
            throw new NuoNiuException("未审核");
        }

        receivableMain.setBookState(1);
        receivableMain.setBookDate(LocalDateTime.now());
        receivableMain.setBookkeeperId(userInfo.getUserId());
        receivableMain.setBookkeeperName(userInfo.getRealName());
        this.updateById(receivableMain);
    }

    @Override
    public ReceivableVerification getVerificationList(String dataSource, Integer orderId) {
        UserInfo userInfo = UserThreadLocal.get();
        ReceivableVerification verification = new ReceivableVerification();
        //获取未核销的应收付单据
        QueryWrapper wrapper = new QueryWrapper();
        if(orderId != null) {
            wrapper.eq("id", orderId);
        }

        wrapper.eq("company_id", userInfo.getCompanyId());
        wrapper.eq("settle_state", 0);
        wrapper.eq("voucher_state", 1);
        wrapper.eq("data_source", dataSource);
        verification.setReceivableMainList(list(wrapper));

        //获取未核销的收付款单
        QueryWrapper rpWrapper = new QueryWrapper();
        rpWrapper.eq("company_id", userInfo.getCompanyId());
        rpWrapper.eq("settle_state", 0);
        rpWrapper.eq("voucher_state", 1);
        if(dataSource.equals("ReceivableVoucher")) {
            rpWrapper.eq("data_source", "ReceiveVoucher");
        } else {
            rpWrapper.eq("data_source", "PaymentVoucher");
        }

        verification.setPaymentMainList(paymentMainService.list(rpWrapper));
        return verification;
    }

    @Override
    public void verification(ReceivableVerification receivableVerification) {
        List<VReceivablePayableMain> receivableMains = receivableVerification.getReceivableMainList();
        List<VReceivePaymentMain> paymentMains = receivableVerification.getPaymentMainList();
        for(VReceivePaymentMain paymentMain : paymentMains) {
            //核销的收/付款金额
            BigDecimal vAmount = new BigDecimal(0.0);
            if(paymentMain.getVerificationAmount() != null) {
                vAmount = paymentMain.getShouldAmount().subtract(paymentMain.getVerificationAmount());
            }

            //剩余未核销金额
            for(VReceivablePayableMain receivableMain : receivableMains) {
                if(receivableMain.getSettleState().equals(1)) {
                    continue;
                }

                BigDecimal vmAmount = receivableMain.getVerificationAmount() != null ? receivableMain.getVerificationAmount() : new BigDecimal(0.0);

                BigDecimal surplusAmount = receivableMain.getPriceTaxTotal().subtract(vmAmount);
                if(surplusAmount.compareTo(vAmount) == 0) { //金额相等，
                    receivableMain.setVerificationAmount(receivableMain.getPriceTaxTotal());
                    receivableMain.setSettleState(1);
                    paymentMain.setVerificationAmount(paymentMain.getAmount());
                    paymentMain.setSettleState(1);
                    break;
                } else if(surplusAmount.compareTo(vAmount) == -1) { //剩余金额小于收/付款金额
                    receivableMain.setVerificationAmount(receivableMain.getPriceTaxTotal());
                    receivableMain.setSettleState(1);
                    paymentMain.setVerificationAmount(paymentMain.getAmount().subtract(vAmount).subtract(surplusAmount));
                    continue;
                } else { //剩余金额大于收/付款金额
                    receivableMain.setVerificationAmount(receivableMain.getPriceTaxTotal().subtract(surplusAmount).add(vAmount));
                    paymentMain.setVerificationAmount(paymentMain.getAmount());
                    paymentMain.setSettleState(1);
                    break;
                }
            }
        }

        paymentMainService.updateBatchById(paymentMains);
        this.updateBatchById(receivableMains);
    }

    @Override
    public List<VReceivePaymentPlan> getPlanList(Integer orderId, Integer type) {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("order_id", orderId);
        wrapper.eq("type", type);
        return planService.list(wrapper);
    }

    @Override
    public void submit(Long id, Integer status) {
        if(null == id || null == status || (null != status && (status > 0 || status < -1 )))throw new NuoNiuException("参数错误");
        UserInfo userInfo = UserThreadLocal.get();
        VReceivablePayableMain orderMain = this.getById(id);
        if( null == orderMain)throw new NuoNiuException("记录不存在");
        //校验状态
        Integer voucherState = orderMain.getVoucherState();
        if(status == 0){
            if (voucherState > -1)throw new NuoNiuException("该单据已经提交");
        }else if(status == -1){
            if (voucherState > 0)throw new NuoNiuException("该单据已经审核,请先弃审");
        }
        //提交
        orderMain.setVoucherState(status);
        orderMain.setSubmitDate(LocalDateTime.now());
        orderMain.setSubmitUserId(userInfo.getUserId());
        orderMain.setSubmitUserName(userInfo.getRealName());
        this.updateById(orderMain);
    }
}
